<?php
// +----------------------------------------------------------------------
// | 支付宝支付
// | putenv,proc_open,proc_get_status
// +----------------------------------------------------------------------

namespace helper\ali;

use AlipayDataDataserviceBillDownloadurlQueryRequest;
use AlipayFundBatchCreateRequest;
use AlipayFundTransOrderQueryRequest;
use AlipayFundTransUniTransferRequest;
use AlipayTradeAppPayRequest;
use AlipayTradeCloseRequest;
use AlipayTradeFastpayRefundQueryRequest;
use AlipayTradePagePayRequest;
use AlipayTradeQueryRequest;
use AlipayTradeRefundRequest;
use AlipayTradeWapPayRequest;
use AopCertClient;
use helper\facade\IPayment;
use stdClass;

require_once __DIR__ . '/sdk/aop/AopCertClient.php';
require_once __DIR__ . '/sdk/aop/request/AlipayTradeAppPayRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayTradeWapPayRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayTradePagePayRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayTradeRefundRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayTradeQueryRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayFundTransUniTransferRequest.php';
require_once __DIR__ . '/sdk/aop/request/AlipayFundTransOrderQueryRequest.php';

class Pay implements IPayment
{
    private array $config;

    public function __construct($config)
    {
        $this->config = [
            'appid' => $config['appid'],
            'notify_url' => $config['notify_url'] ?? '',
            'private_key' => $config['private_key'],// 开发者私钥(应用私钥)
            'payer' => $config['payer'] ?? '',
            'app_cert_path' => $config['app_cert_path'],
            'alipay_cert_path' => $config['alipay_cert_path'],
            'root_cert_path' => $config['root_cert_path'],
        ];
    }

    private function aop()
    {
        $aop = new AopCertClient ();
        $aop->gatewayUrl = 'https://openapi.alipay.com/gateway.do';
        $aop->appId = $this->config['appid'];
        $aop->rsaPrivateKey = $this->config['private_key'];
        $aop->apiVersion = '1.0';
        $aop->signType = 'RSA2';
        $aop->postCharset='utf-8';
        $aop->format='json';
        $aop->isCheckAlipayPublicCert = true;//是否校验自动下载的支付宝公钥证书，如果开启校验要保证支付宝根证书在有效期内
        $aop->appCertSN = $aop->getCertSN($this->config['app_cert_path']);//调用getCertSN获取证书***
        $aop->alipayrsaPublicKey = $aop->getPublicKey($this->config['alipay_cert_path']);//调用getPublicKey从支付宝公钥证书中提取公钥
        $aop->alipayRootCertSN = $aop->getRootCertSN($this->config['root_cert_path']);//调用getRootCertSN获取支付宝根证书***
        return $aop;
    }

    /**
     * page 支付
     * alipay.trade.page.pay(统一收单下单并支付页面接口)
     * @param string $subject 订单标题
     * @param string $out_trade_no 商户订单号
     * @param float $total_amount 订单总金额
     * @param string $return_url 页码返回支付结果
     * @return void
     */
    public function page(string $subject, string $out_trade_no, float $total_amount, string $return_url = '', $time = 600)
    {
        $bizContent = [
            "subject" => $subject,
            "out_trade_no" => $out_trade_no,
            "total_amount" => $total_amount,
            "product_code" => "FAST_INSTANT_TRADE_PAY",
            "expire_time" => date('Y-m-d H:i:s', time() + $time)//订单绝对超时时间
        ];
        $json = json_encode($bizContent);
        $request = new AlipayTradePagePayRequest ();
        $request->setBizContent($json);
        $request->setNotifyUrl($this->config['notify_url']);
        $request->setReturnUrl($return_url);
        echo $this->aop()->pageExecute($request);
    }

    /**
     * wap支付
     * alipay.trade.wap.pay(手机网站支付接口2.0)
     * @param string $subject 订单标题
     * @param string $out_trade_no 商户订单号
     * @param float $total_amount 订单总金额
     * @param string $quit_url 用户付款中途退出返回商户网站的地址
     * @param string $return_url 页码返回支付结果
     * @throws \Exception
     */
    public function wap(string $subject, string $out_trade_no, float $total_amount, string $quit_url, string $return_url)
    {
        $request = new AlipayTradeWapPayRequest ();
        $request->setBizContent("{" .
            "\"subject\":\"$subject\"," .
            "\"out_trade_no\":\"$out_trade_no\"," .
            "\"total_amount\":$total_amount," .
            "\"quit_url\":\"$quit_url\"," .
            "\"product_code\":\"QUICK_WAP_PAY\"" .
            "  }");
        $request->setReturnUrl($return_url);
        $result = $this->aop()->pageExecute($request);
        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * app支付
     * alipay.trade.app.pay(app支付接口2.0)
     * @param string $subject 订单标题
     * @param string $out_trade_no 商户订单号
     * @param float $total_amount 订单总金额
     * @return mixed
     */
    public function app(string $subject, string $out_trade_no, float $total_amount)
    {
        $request = new AlipayTradeAppPayRequest();
        $bizcontent = "{"
            . "\"subject\": \"$subject\","
            . "\"out_trade_no\": \"$out_trade_no\","
            . "\"timeout_express\": \"30m\","
            . "\"total_amount\": \"$total_amount\","
            . "\"product_code\":\"QUICK_MSECURITY_PAY\""
            . "}";
        $request->setNotifyUrl($this->config['notify_url']);//"商户外网可以访问的异步地址"
        $request->setBizContent($bizcontent);
        $response = $this->aop()->sdkExecute($request);
        //htmlspecialchars是为了输出到页面时防止被浏览器将关键参数html转义，实际打印到日志以及http传输不会有这个问题
        //htmlspecialchars($response)
        return $response;//就是orderString 可以直接给客户端请求，无需再做处理。
    }

    /**
     * 异步回调验证
     * @param array $post
     * @return array
     */
    public function verifyNotify(array $post): array
    {
        $flag = $this->aop()->rsaCheckV1($post, NULL, "RSA2");
        //$flag返回是的布尔值，true或者false,可以根据这个判断是否支付成功
        return $flag ? $post : [];
    }

    /**
     * 单笔转账到支付宝账户API列表
     * alipay.fund.trans.uni.transfer(单笔转账接口)
     * 1.单笔转账到支付宝账户接口
     * alipay.fund.trans.uni.transfer
     * alipay_fund_trans_toaccount_transfer(已弃用)
     * 公共错误码:https://docs.open.alipay.com/common/105806
     * @param string $out_trade_no 订单号
     * @param float $amount 金额
     * @param string $account 账号
     * @param string $name 姓名
     * @param string $remark 备注
     * @return array
     */
    public function transfer(string $out_trade_no, float $amount, string $account, string $name, string $remark): array
    {
        $request = new AlipayFundTransUniTransferRequest();
        $bizContent = [
            "out_biz_no" => $out_trade_no,
            "biz_scene" => "DIRECT_TRANSFER",// DIRECT_TRANSFER,PERSONAL_COLLECTION,DIRECT_TRANSFER
            "payee_info" => [
                "identity" => $account,
                "identity_type" => "ALIPAY_LOGON_ID", // ALIPAY_USER_ID, ALIPAY_LOGON_ID, BANKCARD_ACCOUNT
                "name" => $name,
                //"bankcard_ext_info" => [
                //    "inst_name" => "",
                //    "account_type" => "2"
                //]
            ],
            "trans_amount" => $amount,
            "product_code" => "TRANS_ACCOUNT_NO_PWD",// TRANS_ACCOUNT_NO_PWD, STD_RED_PACKET, TRANS_BANKCARD_NO_PWD
            "order_title" => $remark,
            "remark" => $remark,
//    "business_params" => '{"payer_show_name_use_alias":"true"}',
//    "sign_data" => [
//        "ori_out_biz_no" => $batch_no,
//        "ori_sign_type" => "RSA2",
//        "ori_sign" => "EqHFP0z4a9iaQ1ep==",
//        "ori_char_set" => "UTF-8",
//        "partner_id" => "", // 签名被授权方支付宝账号ID
//        "ori_app_id" => $appid
//    ]
        ];

        $request->setBizContent(json_encode($bizContent));
        $responseResult = $this->aop()->execute($request);
        $responseApiName = str_replace(".","_",$request->getApiMethodName())."_response";
        $response = $responseResult->$responseApiName;
        if(!empty($response->code)&&$response->code==10000){
            $status = 1;
            $msg  = $response->msg;
        }
        else{
            $status = 0;
            $msg  = $response->msg . ' ' . $response->sub_msg;
        }
        $data = (array)$response;
        return ['status' => $status, 'msg'  => $msg, 'data' => $data];
    }

    /**
     * 单笔转账到支付宝账户API列表
     * 2.查询转账订单接口(alipay.fund.trans.order.query)
     * @param string $out_trade_no 商户转账唯一订单号
     *
     */
    public function transfer_query($out_trade_no)
    {
        $request = new AlipayFundTransOrderQueryRequest ();
        // order_id:支付宝转账单据号
        $request->setBizContent("{" .
            "\"out_biz_no\":\"$out_trade_no\"," .
            "\"order_id\":\"\"" .
            "  }");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            //echo "成功";
            return $result->$responseNode;
        } else {
            //echo "失败";
            return $result->$responseNode;
        }
    }

    /**
     * 批量付款到户有密
     * 1.alipay.fund.batch.create(批次下单接口)
     * @return bool
     * @throws \Exception
     */
    public function batchCreate($batch_no)
    {
        $request = new AlipayFundBatchCreateRequest();
        $bizContent = [
            "out_batch_no" => $batch_no,
            "product_code" => "BATCH_PAY_V2",// TRANS_BANKCARD_NO_PWD BATCH_PAY_V2
            "biz_scene" => "MESSAGE_BATCH_PAY",// DIRECT_TRANSFER MESSAGE_BATCH_PAY
            "order_title" => "代发",
            "total_trans_amount" => "1",
            "total_count" => "1",
            "trans_order_list" => [[
                "out_biz_no" => $batch_no,
                "trans_amount" => "1",
                "payee_info" =>     [
                    "identity" =>  "6230520670010196770",
                    "identity_type" => "BANKCARD_NO",// BANKCARD_ACCOUNT
                    "name" => "王敏",
                    "bankcard_ext_info" => [
                        "inst_name" => "中国农业银行",// $_REQUEST['inst_name']
                        "account_type" => "2",// $_REQUEST['account_type']
                        //"inst_province" => "",
                        //"inst_city" => "",
                        //"inst_branch_name" => "",
                        //"bank_code" => ""
                    ],
                    "merchant_user_info" => '{"merchant_user_id": "123456"}',
                    "ext_info" => '{"alipay_anonymous_uid": "2088123412341234"}'
                ],
                "order_title" => "1",// $conf['goodsname']
                "remark" => "1",// $conf['goodsname']
                // "business_params" => '{"sub_biz_scene": "BAOXIAO","withdraw_timeliness": "T0"}',
                //"passback_params" => '{"MERCHANT_PAYER_ID": "1234567890","MERCHANT_PAYER_PHONE": "15012341234"}'
            ]],
            //"business_params" => '{"sub_biz_scene": "REDPACKET"}',
            //"passback_params" => '{"merchantBizType": "pay"}',
            "remark" => "1"// $conf['goodsname']
        ];

        $request->setBizContent(json_encode($bizContent));

        $responseResult = $this->aop()->execute($request);
        $responseApiName = str_replace(".","_",$request->getApiMethodName())."_response";
        $response = $responseResult->$responseApiName;

        if (!empty($resultCode) && $resultCode == 10000) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 批量付款到户有密
     * 2.alipay.fund.trans.page.pay(资金转账页面支付接口)
     * @return bool
     * @throws \Exception
     */
    function batchPay()
    {
        $body = [
            "out_batch_no" => "",
            "trans_amount" => "",
            "product_code" => "BATCH_PAY_V2",
            "biz_scene" => "MESSAGE_BATCH_PAY",
            "order_title" => "",
            "order_id" => "",
            "payer_info" => [
                "identity" =>  "",
                "identity_type" => "",// BANKCARD_ACCOUNT
                "name" => "",
                //"bankcard_ext_info" => [
                    //"inst_name" => "",// $_REQUEST['inst_name']
                    //"account_type" => "",// $_REQUEST['account_type']
                    //"inst_province" => "",
                    //"inst_city" => "",
                    //"inst_branch_name" => "",
                    //"bank_code" => ""
                //],
                "merchant_user_info" => '{"merchant_user_id": "123456"}',
                "ext_info" => '{"alipay_anonymous_uid": "2088123412341234"}'
            ],
            "payee_info" => [
                "identity" =>  "6230520670010196770",
                "identity_type" => "BANKCARD_NO",// BANKCARD_ACCOUNT
                "name" => "王敏",
                "bankcard_ext_info" => [
                    "inst_name" => "中国农业银行",// $_REQUEST['inst_name']
                    "account_type" => "2",// $_REQUEST['account_type']
                    //"inst_province" => "",
                    //"inst_city" => "",
                    //"inst_branch_name" => "",
                    //"bank_code" => ""
                ],
                "merchant_user_info" => '{"merchant_user_id": "123456"}',
                "ext_info" => '{"alipay_anonymous_uid": "2088123412341234"}'
            ],
            "time_expire" => "",
            "refund_time_expire" => "",
            "passback_params" => '{"merchantBizType": "peerPay"}',
            "business_params" => '{"sub_biz_scene": "REDPACKET"}',
            "remark" => "发福利"
        ];

        $request = new AlipayFundTransPagePayRequest ();
        $request->setBizContent(json_encode($body));
        $result = $this->aop()->pageExecute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            return true;
        } else {
            return false;
        }
    }

    // alipay.fund.batch.order.changed(批次单据状态变更通知)

    /**
     * 批量付款到户有密
     * alipay.fund.batch.detail.query(批量转账明细查询接口)
     */
    public function batchQuery()
    {
        $request = new AlipayFundBatchDetailQueryRequest ();
        $request->setBizContent("{" .
            "\"batch_no\":\"hk201703130008\"," .
            "\"biz_code\":\"BATCH_TRANS_ACC\"," .
            "\"biz_scene\":\"GLOBAL\"," .
            "\"sign_principal\":\"test@alipay.com\"," .
            "\"detail_no\":\"001\"," .
            "\"detail_status\":\"INIT\"," .
            "\"payee_account\":\"zhangsan@alipay.com\"," .
            "\"page_num\":1," .
            "\"product_code\":\"BATCH_PAY_V2\"," .
            "\"out_batch_no\":\"201801310127742502\"," .
            "\"out_biz_no\":\"2018999960760005838333\"," .
            "\"page_size\":\"1\"" .
            "  }");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }

    /**
     * 批量付款到户有密
     * alipay.fund.batch.close(批量转账关单接口)
     */
    public function batchClose()
    {
        $request = new AlipayFundBatchCloseRequest ();
        $request->setBizContent("{" .
            "      \"product_code\":[" .
            "        \"BATCH_PAY_V2\"" .
            "      ]," .
            "\"biz_scene\":\"MESSAGE_BATCH_PAY\"," .
            "\"batch_trans_id\":\"201801310127742502\"" .
            "  }");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }

    /**
     * 批量付款到户有密
     * alipay.fund.batch.app.pay(批量有密跳端支付)
     */
    public function batchAppPay()
    {
        $request = new AlipayFundBatchAppPayRequest ();
        $request->setBizContent("{" .
            "  \"batch_trans_id\":\"202103196260366808\"" .
            "}");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }

    /**
     * H5扫描转账到银行
     * https://opendocs.alipay.com/open/common/transfertocard
     */
    public function payBank()
    {
        $param = [
            'appId' => '',   // 应用ID -默认
            'actionType' => 'toCard',  // 转账类型 toCard-到银行卡
            'sourceId' => 'bill',  // 未知
            'cardNo' => '6217000030001234567',  // 银行卡号
            'bankAccount' => urlencode(''),  // 银行账户
            'money' => '0.01',  // 转账金额
            'amount' => '0.01',  // 转账额度
            'bankMark' => 'CCB',  // 银行代号 -可选
            'bankName' => urlencode(''),  // 银行名称
        ];
        $param = http_build_query($param);
        return "alipays://platformapi/startapp?" . $param;
    }

    public function back()
    {
        return [
            "中国工商银行 ICBC Industrial and Commercial Bank of China",
            "中国建设银行 CCB China Construction Bank",
            "汇丰银行 HSBC Hongkong and Shanghai Banking Corporation",
            "中国银行 BC Bank of China",
            "中国农业银行 ABC Agricultural Bank of China",
            "交通银行 BC Bank of Communications",
            "招商银行 CMB China Merchants Bank",
            "中国民生银行 CMB China Minsheng Bank",
            "上海浦东发展银行SPDB Shanghai Pudong Development Bank",
            "中信银行 China CITIC Bank",
            "中国光大银行 CEB China Everbright Bank",
            "华夏银行 HB Huaxia Bank",
            "广东发展银行 GDB Guangdong Development Bank",
            "深圳发展银行 SDB Shenzhen Development Bank",
            "兴业银行 CIB China's Industrial Bank",
            "国家开发银行 CDB China Development Bank",
            "中国进出口银行 EIBC Export-Import Bank of China",
            "中国农业发展银行 ADBC Agricultural Development of China"
        ];
    }

    /**
     * APP支付
     * alipay.trade.query(统一收单线下交易查询)
     * out_trade_no:订单支付时传入的商户订单号,和支付宝交易号不能同时为空。
     * trade_no:支付宝交易号，和商户订单号不能同时为空
     * org_pid:银行间联模式下有用，其它场景请不要使用；
     */
    public function query($param)
    {
        $request = new AlipayTradeQueryRequest ();
        $request->setBizContent("{" .
            "  \"out_trade_no\":\"20150320010101001\"," .
            "  \"trade_no\":\"2014112611001004680 073956707\"," .
            "  \"org_pid\":\"2088101117952222\"," .
            "  \"query_options\":[" .
            "    \"trade_settle_info\"" .
            "  ]" .
            "}");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }

    /**
     * APP支付
     * alipay.trade.close(统一收单交易关闭接口)
     */
    public function close($param)
    {
        $object = new stdClass();
        $object->trade_no = '2013112611001004680073956707';
        $json = json_encode($object);
        $request = new AlipayTradeCloseRequest();
        $request->setBizContent($json);
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }

    /**
     * APP支付
     * alipay.trade.refund(统一收单交易退款接口)
     */
    public function refund(string $out_trade_no, $refund_amount, $total_amount): bool
    {
        $object = new stdClass();
        $object->out_trade_no = $out_trade_no;
        $object->refund_amount = $refund_amount;
        $object->out_request_no = $out_trade_no;
        //// 返回参数选项，按需传入
        //$queryOptions =[
        //   'refund_detail_item_list'
        //];
        //$object->query_options = $queryOptions;
        $json = json_encode($object);
        $request = new AlipayTradeRefundRequest();
        $request->setBizContent($json);

        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * APP支付
     * alipay.trade.fastpay.refund.query(统一收单交易退款查询)
     */
    public function refund_query(string $out_trade_no): array
    {
        $object = new stdClass();
        $object->trade_no = $out_trade_no;
        $object->out_request_no = $out_trade_no;
        //// 返回参数选项，按需传入
        //$queryOptions =[
        //    'refund_detail_item_list'
        //];
        //$object->query_options = $queryOptions;
        $json = json_encode($object);
        $request = new AlipayTradeFastpayRefundQueryRequest();
        $request->setBizContent($json);
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            return (array)$result->$responseNode;
        } else {
            return [];
        }
    }

    /**
     * APP支付
     * alipay.data.dataservice.bill.downloadurl.query(查询对账单下载地址)
     * bill_type:账单类型 trade / signcustomer
     * bill_date:账单时间 yyyy-MM-dd / yyyy-MM
     * 注意:不能查询当日或者是当月的账单
     */
    public function alipay_data_dataservice_bill_downloadurl_query($param)
    {
        $request = new AlipayDataDataserviceBillDownloadurlQueryRequest ();
        $request->setBizContent("{" .
            "\"bill_type\":\"trade\"," .
            "\"bill_date\":\"2016-04-05\"" .
            "  }");
        $result = $this->aop()->execute($request);

        $responseNode = str_replace(".", "_", $request->getApiMethodName()) . "_response";
        $resultCode = $result->$responseNode->code;
        if (!empty($resultCode) && $resultCode == 10000) {
            echo "成功";
        } else {
            echo "失败";
        }
    }
}
